package piwan;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

import org.fenggui.Display;
import org.fenggui.FengGUI;
import org.fenggui.binding.render.jogl.JOGLBinding;
import org.fenggui.theme.XMLTheme;

import com.sun.opengl.util.Animator;
//import com.sun.opengl.util.FPSAnimator;

public abstract class MainWindow implements GLEventListener {
	
	public MainWindow(String title) {
		canvas = new GLCanvas();
		canvas.addGLEventListener(this);
		
		frame = new JFrame(title);
		frame.getContentPane().add(canvas);
		frame.setUndecorated(true);
		frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		
		animator = new Animator(canvas);
		animator.start();
		
		fls = new ArrayList<FrameListener>();
		rmfls = new LinkedList<FrameListener>();
		handler = new CompHandler();
		dispatcher = new EventDispatcher(canvas,handler);
	}
	
	public void resetTimer() {
		lastTime = System.nanoTime();
	}
	
	public void loadTheme(String themeFile) {
		try {	
			FengGUI.setTheme( new XMLTheme(themeFile) );
		} catch( Exception e ) {
			die(e);
		}
	}
	
	public void addFrameListener( FrameListener newFl ) { fls.add(newFl); }
	public void addEventHandler( EventHandler newHdl ) { handler.add(newHdl); }
	
	public void removeFrameListener( FrameListener fl ) { rmfls.add(fl); }
	public void removeEventHandler( EventHandler hdl ) { handler.remove(hdl); }
	
	public void requestFocus() { canvas.requestFocus(); }
	public int getWidth() { return viewport[2]; }
	public int getHeight() { return viewport[3]; }
	public int[] getViewport() { return viewport; }
	public GL getGL() { return gl; }
	public GLU getGLU() { return glu; }
	public Display getDisplay() { return display; }
	
	public static void die( Exception e ) {
		System.out.println("Unrecoverable exception of type " + e.getClass().getName());
		System.out.println("Message: " + e.getMessage());
		System.out.println("Stack trace:");
		e.printStackTrace();
		System.exit(-1);
	}
	
	
	// GLEventListener
	public void display(GLAutoDrawable arg0) {
		long newTime = System.nanoTime();
		double delta = (double)(newTime-lastTime)/1e9;
		lastTime = newTime;
		
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		dispatcher.dispatch();
		fls.removeAll(rmfls);
		rmfls.clear();
		for(FrameListener fl: fls)
			fl.newFrame(delta);
		display.display();
		try {
			Thread.sleep(10);
		} catch(Exception e) {}
	}
	
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
		throw new UnsupportedOperationException("Cannot change display.");
	}
	
	public void init(GLAutoDrawable drawable) {
		gl = drawable.getGL();
		glu = new GLU();
		
		display = new Display( new JOGLBinding(canvas) );
		new ConsumerBinding(canvas,display);
		dispatcher.enable();
		
		lastTime = System.nanoTime();
		
		viewport = new int[4];
		viewport[0] = 0;
		viewport[1] = 0;
		viewport[2] = canvas.getWidth();
		viewport[3] = canvas.getHeight();
		
		glInited();
		requestFocus();
	}
	
	public void reshape(GLAutoDrawable dr, int x, int y, int w, int h) {
		gl.glViewport(0, 0, w, h);
		viewport[2] = w;
		viewport[3] = h;
	}
	
	protected abstract void glInited();
	protected Display		display = null;
	protected GL			gl = null;
	protected GLU			glu = null;
	
	private GLCanvas	canvas = null;
	private JFrame		frame = null;
	
	private int[]		viewport = null;
	private Animator	animator = null;
	private long		lastTime = 0;
	
	
	
	private ArrayList<FrameListener>	fls;
	private Queue<FrameListener>		rmfls;
	private EventDispatcher				dispatcher;
	private CompHandler					handler;

	
}
